perm filename SPASM.RPG[UP,DOC] blob sn#512362 filedate 1980-05-21 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00007 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00002 00002	General Purpose Lisp Assembler Design
C00007 00003	The SPASM macro facilty
C00010 00004	Manipulating the Symbol Table
C00015 00005	Pseudo-ops
C00017 00006	Running SPASM
C00019 00007	The .OBJ file
C00024 ENDMK
C⊗;
General Purpose Lisp Assembler Design

This assembler was written by RPG one day in 1980 while at SAIL.  Its
first incarnation was in MacLisp later it was (will be?) converted to Lisp
Machine LISP.

The assembler described here is intended to be used in the standard Lisp
enviroment thus making much of the power of Lisp available at the assembly
language level.  Ease in the implementation of this assembler was a
primary design consideration, so many fancy features may not be present.
Hopefully the facilities of Lisp can be used to compensate for this.

The source object is an input stream.  It is comprised of symbols, which
are treated as labels, and function forms, which are evaluated to produce
object values, assign values to symbols and do whatever else is
interesting.

The first assembly pass works down the Assembly List mapping each element
according to its nature.  If the element is a symbol (ie a label) it is
assigned the current assembler address.  Otherwise the element is
evaluated and replaced by any non-NIL value returned.  The current address
is advanced by 0 if the value is NIL, by 1 if it is an atom.  If it is a
list it is advanced by the length of the list unless the car is the symbol
SPECIAL-ASSEMBLY-FORM, in which case it is advanced by the second element
in the list.  (As a special case, the symbol COMMENT is mapped into NIL to
provide for the standard Lisp COMMENT function). In addition, if the first
element of the list is a number, the current address is advanced by that
amount.  The normal thing, then, is for the form to return something like:
(n second-pass-form) where n is the length of the eventual instruction
with operands.

The first pass also distinguishes the special form (DECLARE ...) which is
like a FEXPR and allows special actions to take place without affecting
the input stream. Mainly it is intended for symbol table manipulations, to
be described below.

The second pass works down the list produced by the first pass.  If an
element is an atom its value is returned.  If it is a list all the
elements in it are evaluated, and a list of their values is returned,
unless the car is the symbol SPECIAL-ASSEMBLY-FORM, in which case all of
the elements in the cdr are evaluated and the values of the cddr are
assembled into a list whose length is the value of the cadr.  The original
element is replaced by a list whose car is the current address and the cdr
is the returned value, unless the specified length was zero, in which case
the original element is deleted.  The list produced by these replacements
is called a Load List.

The intention of this process is that the first pass does a kind of
macro-expansion and the second pass actually generates the data to be
loaded.  Expressions are evaluated in an environment where symbols are
lambda bound to their assigned values, thus allowing Lisp expressions to
refer to them directly.

The SPASM macro facilty

If the first pass does not recognize the form as something applicable, it
tries to interpret it as a macro, which can be define in several standard
ways.  The difference between the standard LISP macro facility and this is
that this actually splices the result into the input stream so that any
number of actual tokens can be inserted for later consideration by the
first pass. Heres some examples of definitions showing some nifty
features:

(defmac jsp (op1 op2)
 ((lambda (tag)
	  `((movppa ,op1 ,tag)
	    (jmpa () ,op2)
	    ,tag))
  (instruction-gentag)))

This one defines JSP in a machine that doesn't have it. The machine does
have a MOVPC type instruction (movppa) which puts the address of TAG in
OP1, JMPs to OP2, and inserts the TAG after this sequence.

(define movadr (op1 op2)
	(movppa op1 op2))

This one defines an instruction that moves the addrress OP2 into OP1.

(defmac jmpn (displacement) 
	((lambda (tag instructions) 
	 (do ((i (1- (eval displacement)) (1- i)) 
	      (a () a)) 
	     ((< i 1) (setq instructions 
			    (reverse (cons tag a)))) 
	     ((lambda (next) 
		      (cond ((atom next) (setq i (1+ i)))) 
		      (setq a (cons next a))) 
	      (next-token))) 
	 `((jmpa () ,tag) . ,instructions))
	 (instruction-gentag) ())) 

This one is designed for a machine that doesn't have a fixed instruction
length and is supposed to jump over n instructions. It does it by gobbling
down n instructions from the instruction stream (!) using (next-token) and
then placing a JMPA to a generated tag.

(defmac flush-this ()())

(define flush-this-one ())

These two just flush the instruction.

Manipulating the Symbol Table

The symbol table is manipulated by a bunch of primitives normally designed
to be used inside a DECLARE (but which usually work anywhere. They are
ASSIGN, ASSIGN-NOW, PROTECTED-ASSIGN, PROTECTED-ASSIGN-NOW,
PERMANENT-ASSIGN, PERMANENT-ASSIGN-NOW, PROTECT, PERMANENT, and UNPROTECT.

This plethora is explained by noting that there are 4 types of symbol
table entries: TAG, VALUE, PROTECTED, and PERMANENT. A TAG is generated
when tags are parsed by the assembler. There is no good way for the user
to change these values. Code can refer to them, however. A VALUE is
created by (ASSIGN-NOW <var1> <val1> ... <varn> <valn>) where each vari is
not evaluated, but each vali is (in the current environment.  ASSIGN is
like ASSIGN-NOW but waits until the second pass to be evaluated.  In fact,
this is done by placing an ASSIGN-NOW in the instruction list for the
second pass. PROTECTED-ASSIGN and PROCTECTED-ASSIGN-NOW are like ASSIGN, but
create PROTECTED entries. Protected entries complain if you randomly try
to change their values, but you always can in the end. PERMANENT-ASSIGN
and PERMANENT-ASSIGN-NOW make permanent entries which cannot be changed at
all.

In addition, you can change the type of an entry with PROTECT, PERMANENT,
and UNPROTECT in the obvious way.

The symbol table, SPASM:SYMBOL-TABLE, has the form:
   	
	(...
	 (<symbol> . (<value> <type> <location>))
	 ...)

where <symbol> is the symbol in question, <value> is its value if it has
one, <type> is one of: TAG, VALUE, FORWARD, PROTECTED, and PERMANENT. If
it is TAG, then location is the location of the tag, if it is FORWARD,
then <value> is the name of the tag this tag is aliased to. If it is
either VALUE, PROTECTED, or PERMANENT, then <value> is its value.

Pseudo-ops
There also exist various other predefined "psuedo-ops".  Many of these
require arguments which can be evaluated during the first pass.  These
utilitys are as follows:

(CURRENT-ASSEMBLY-ADDRESS optional-first-pass-value)
	Sets the next location for assembly to its argument if present.
	Returns the address of the next location for assembly.

(RELATIVE address)
	Is a macro which expands into (- ADDRESS (CURRENT-ASSEMBLY-ADDRESS))

(SPECIAL-ASSEMBLY-FORM size form1 form2 ...)
	During the first pass this is replaced by itself but with all the
	arguments evaluated.  During the second pass the forms are
	evaluated, appended and checked for size.

(PASS1-PAUSE)
	Causes a pause at the end of the first pass.

Running SPASM

To run SPASM you say R SPASM. In your aliased directory you should have a
file called SMACRO.INI which contains MacLisp code that makes up your own
assembler. This file can load files and can have code there directly. Also,
you can have macro definitions without DECLAREs, which are REQUIRED in  source
files (i.e. (DECLARE (DEFINE ...)(DEFMAC ...) ...)).
You are then prompted for file specs, which look like:

	{dest}←source{(switches)}

where dest is optional and defaults to filename(source).OBJ. The "←" is
optional too if dest is left out. The switches can be T and L, each meaning
make a listing file, called filename(source).LST. 

So you can say:

	zte.sch[foo,bar←mum.ble[baz,mum](l)

to the poor thing.

The .OBJ file
Currently the .OBJ file is just the Load List (until someone wants
something else. If the variable OUTPUT-ROUTINE is non-nil, it will be
taken as the name of a function of two arguments, a list: (address . bytes)
and an output file object, it will be called ala 

	(FUNCALL SPASM:OUTPUT-ROUTINE bytes file).

Finally, some processors require some extra passes in order to, for
instance, judge jump lengths correctly. To accompish this there is a
global variable, SPASM:PASS2, which, when non-null, contains the name of a
routine of no arguments which will process the instructions.  To end these
passes, the routine must return a non-null; if it returns NIL, then the
pass is repeated (i.e. (WHILE (NOT (FUNCALL SPASM:PASS2)) DO ())) The list
of instructions is called SPASM:INSTRUCTIONS, and MAY CONTAIN A NULL ENTRY
at the end. Otherwise the format of the list is:  

	(...  (<location> <tag><pass1-result> <orig-inst>) 
	 ...)

<location> is the byte address of the beginning of the instruction, <tag>,
if non-null is the tag associated with the instruction (others tags can be
aliased to this one in the symbol table. <pass1-result> is the form that
the last pass will eval. <orgi-inst> is the original form that was here.
The routine SPASM:PASS2 should setq SPASM:INSTRUCTIONS to whatever is
desired for the last pass. Any NULLs, like the last one, can be flushed

Some
global variables in the first pass are:

spasm:loc 			current location
spasm:instruction 		current instruction
spasm:macro-list		list of user defined macros
spasm:words-generated		total number of bytes so far
spasm:pass1-pause		flag to pause after first pass1
spasm:symbol-table		current symbol table
spasm:input-stream 		instruction stream (internal format)
spasm:output-routine		when non-null has the value of the output routine
spasm:pass2			when non-null has the value of the pass2 routine

Here's a Handy Dandy declaration for your SPASM files. Note that Relative
is a MACRO:

;;; Macro: Relative 

(declare (*expr rel pass1-pause zbreak)
	 (*lexpr current-assembly-address)
	 (*fexpr assign-now protected-assign-now permanent-assign-now
		 assign protected-assign permanent-assign
		 unprotect-assignments assignment-unprotect
		 protect permanent unprotect))